#codeing = utf-8
#!/usr/bin/python3

import sys
import os

app_name = 'tools eds parse'
app_softverision = 123

def getinfo_subindex(file_eds_buf, Index, subindex):
  if file_eds_buf.has_option(Index, subindex) == False:
    reinfo_subindex = ''
  else:
    reinfo_subindex = file_eds_buf.get(Index, subindex)
  return reinfo_subindex

def get_datatype(datatype):#数据类型
  if datatype[0:2] == '0x':
    datatype = int(datatype,16)
  else:
    datatype = int(datatype)

  if datatype == 1:
    return 'BOOLEAN'
  elif datatype == 2:
    return 'INTEGER8'
  elif datatype == 3:
    return 'INTEGER16'
  elif datatype == 4:
    return 'INTEGER32'
  elif datatype == 5:
    return 'UNSIGNED8'
  elif datatype == 6:
    return 'UNSIGNED16'
  elif datatype == 7:
    return 'UNSIGNED32'
  elif datatype == 9:
    return 'VISIBLE_STRING'
  elif datatype == 27:
    return 'UNSIGNED64'
  else:
    return 'none'

def get_objtype(objtype):#obj类型
  if objtype[0:2] == '0x': #objtype 有可能是16进制 0xaa
    objtype = int(objtype,16)
  else:
    objtype = int(objtype)

  if objtype == 7:
    return 'VAR'
  elif objtype == 8:
    return 'ARRAY'
  elif objtype == 9:
    return 'RECORD'
  else:
    return 'none'

def get_objmap(objmap):
  if objmap=='1':
    return 'yes'
  else:
    return 'no'

def get_pdomap_datatype(datatype):
  if datatype == '08':
    return 'UNSIGNED8'
  elif datatype == '10':
    return 'UNSIGNED16'
  elif datatype == '20':
    return 'UNSIGNED32'
  else:
    return 'none'

def eds_parse(list_edsbuf, str_objname):
  result = ''
  if list_edsbuf.has_section(str_objname) == False:
    sys.exit(0) #没有检测到 MandatoryObjects 退出
  #获取OptionalObjects参数个数
  obj_optional_sum = getinfo_subindex(list_edsbuf, str_objname, 'SupportedObjects')
  #obj_optional_sum 有可能是16进制 0xaa
  if obj_optional_sum[0:2] == '0x':
    obj_optional_sum = int(obj_optional_sum,16)
  #获取通讯参数列表
  list_optional = list_edsbuf.options(str_objname)
  print('>', '[param num]', str_objname, obj_optional_sum)
  #读取索引列表，写入文件
  for index in range(int(obj_optional_sum)):
    str_eds_index_code = getinfo_subindex(list_edsbuf, str_objname, list_optional[index+1])
    str_eds_index_name = getinfo_subindex(list_edsbuf, str_eds_index_code[2:6], 'ParameterName')
    #print(str_eds_index_code, str_eds_index_name)
    str_eds_index_str = '### ' + str_eds_index_code + ' - ' + str_eds_index_name + '\n'
    result = result + str_eds_index_str
    objtype = getinfo_subindex(list_edsbuf, str_eds_index_code[2:6], 'ObjectType')
    alias_objtype = get_objtype(objtype)
    #objtype 有可能是16进制 0xaa
    if objtype[0:2] == '0x':
      objtype = int(objtype,16)
    else:
      objtype = int(objtype)
    #print(obj_optional_sum, index, str_eds_index_name, str_eds_index_code)

    if objtype == 7: #根据ObjectType处理
      list_subinx_code = str_eds_index_code[2:6]
      datatype = getinfo_subindex(list_edsbuf, list_subinx_code, 'DataType')
      alias_datatype  = get_datatype(datatype)
      accesstype = getinfo_subindex(list_edsbuf, list_subinx_code, 'AccessType')
      #print(list_subinx_code)
      defval = getinfo_subindex(list_edsbuf, list_subinx_code, 'DefaultValue')
      pdomap = getinfo_subindex(list_edsbuf, list_subinx_code, 'PDOMapping')
      alias_map = get_objmap(pdomap)
      #print(str_eds_index_code, list_subinx_code, str_eds_index_name)
      str_tmp = '|' + '0x0' + '|' + alias_datatype + '|' + accesstype + '|' + defval + '|' + alias_map + '|\n'
      result = result + '|Sub Inx|Data Type|Access Type|Default Value|PDO Mapping|\n'
      result = result + '| ---- | ------ | -------- | ---------- | -------- |\n'
      result = result + str_tmp
    elif objtype == 9 or objtype == 8:
      subnumber = getinfo_subindex(list_edsbuf, str_eds_index_code[2:6], 'SubNumber')#获取子索引个数
      #obj_optional_sum 有可能是16进制 0xaa
      if subnumber[0:2] == '0x':
        subnumber = int(subnumber,16)
      result = result + '|Sub Inx|Parameter Name|Data Type|Access Type|Default Value|PDO Mapping|\n'
      result = result + '| ---- | ----------- | ------ | -------- | ---------- | -------- |\n'
      subinxid_offset = 0
      for subindexsp in range(int(subnumber)):
        subinxid = subindexsp + subinxid_offset
        if subinxid>15 :
          eds_index_sub_id_str = '{:02X}'.format(subinxid)# subinxid 应为2Byte的16进制
        else:
          eds_index_sub_id_str = '{:01X}'.format(subinxid)# subinxid 应为2Byte的16进制

        list_subinx_code = str_eds_index_code[2:6] + 'sub' + eds_index_sub_id_str
        check_subinx_cnt = 0 #检查子索引的次数，大于阈值时，退出循环
        while list_edsbuf.has_section(list_subinx_code) == False : 
          #没有检测到 list_subinx_code  subinxid+1 处理
          #eds文件错误，不存在该索引
          subinxid_offset = subinxid_offset + 1 #子索引 偏移 原因：子索引可能不连续
          if check_subinx_cnt>256 :#检查子索引的次数，大于阈值时，退出循环
            break
          else:
            check_subinx_cnt = check_subinx_cnt + 1
          subinxid = subindexsp + subinxid_offset
          if subinxid>15 :
            eds_index_sub_id_str = '{:02X}'.format(subinxid)# subinxid 应为2Byte的16进制
          else:
            eds_index_sub_id_str = '{:01X}'.format(subinxid)# subinxid 应为2Byte的16进制
          list_subinx_code = str_eds_index_code[2:6] + 'sub' + eds_index_sub_id_str
          continue

        if list_edsbuf.has_section(list_subinx_code) == True:
          paramname = getinfo_subindex(list_edsbuf, list_subinx_code, 'ParameterName')
          datatype = getinfo_subindex(list_edsbuf, list_subinx_code, 'DataType')
          alias_datatype  = get_datatype(datatype)
          accesstype = getinfo_subindex(list_edsbuf, list_subinx_code, 'AccessType')
          defval = getinfo_subindex(list_edsbuf, list_subinx_code, 'DefaultValue')
          pdomap = getinfo_subindex(list_edsbuf, list_subinx_code, 'PDOMapping')
          alias_map = get_objmap(pdomap)
          str_tmp = '|' + '0x' + eds_index_sub_id_str + '|' + paramname + '|' + alias_datatype + '|' + accesstype + '|' + defval + '|' + alias_map + '|\n'
          result = result + str_tmp
  return result

def eds_pdoparse_styleII(list_edsbuf, xpdo):
  result = ''
  eds_tpdo_num = getinfo_subindex(list_edsbuf, 'DeviceInfo', 'NrOfTXPDO') #获取tpdo数量 MAP-1A00
  eds_rpdo_num = getinfo_subindex(list_edsbuf, 'DeviceInfo', 'NrOfRXPDO') #获取rpdo数量 MAP-1600
  #获取 1A00 - 1A03 中 参数映射个数 即1A00 - 1A03中的子索引个数，分别读出，解析出 索引，子索引，数据类型
  #获取 1600 - 1603 中 参数映射个数 即1600 - 1603中的子索引个数
  #str_tuple = ('PDOIndex', 'PDOSubInx', 'MapIndex', 'MapSubInx', 'MapDataType') #创建一个元祖
  if xpdo=='TPDO':
    eds_pdo_option_num = eds_tpdo_num
    print('>', '[TPDO num]', eds_tpdo_num)
  else:
    eds_pdo_option_num = eds_rpdo_num
    print('>', '[RPDO num]', eds_rpdo_num)

  for index_pdo in range (int(eds_pdo_option_num)): #遍历tpdo数量
    if xpdo=='TPDO':
      #获取 1A00 - 1A03
      pdo_map_id = '1A0' + str(index_pdo)
      str_tmp = '### TPDO' + str(index_pdo+1) + ' - 0x' + pdo_map_id + '\n'
      result = result + str_tmp
      str_COBID = '180' + str(index_pdo) + 'sub1'
      str_transtype = '180' + str(index_pdo) + 'sub2'#查询传输类型
      str_inhibittime = '180' + str(index_pdo) + 'sub3'#查询抑制时间
      str_eventtimer = '180' + str(index_pdo) + 'sub5'#查询事件定时器
      str_syncstartval = '180' + str(index_pdo) + 'sub6'#查询同步起始值
    else:
      #获取 1600 - 1603
      pdo_map_id = '160' + str(index_pdo)
      str_tmp = '### RPDO' + str(index_pdo+1) + ' - 0x' + pdo_map_id + '\n'
      result = result + str_tmp
      str_COBID = '140' + str(index_pdo) + 'sub1'
      str_transtype = '140' + str(index_pdo) + 'sub2'#查询传输类型
      str_inhibittime = '140' + str(index_pdo) + 'sub3'#查询抑制时间
      str_eventtimer = '140' + str(index_pdo) + 'sub5'#查询事件定时器
      str_syncstartval = '140' + str(index_pdo) + 'sub6'#查询同步起始值

    result = result + '#### 参数' + '\n'
    #查询COB-ID
    str_tmp = getinfo_subindex(list_edsbuf, str_COBID, 'DefaultValue')
    str_tmp = '> Sub1 COB-ID : ' + str_tmp + '\n'
    result = result + str_tmp
    #查询传输类型
    str_tmp = getinfo_subindex(list_edsbuf, str_transtype, 'DefaultValue')
    str_tmp = '> Sub2 Transmission : ' + str_tmp + '\n'
    result = result + str_tmp
    #查询抑制时间
    str_tmp = getinfo_subindex(list_edsbuf, str_inhibittime, 'DefaultValue')
    str_tmp = '> Sub3 Inhibit Time (100us) : ' + str_tmp + '\n'
    result = result + str_tmp
    #查询事件定时器
    str_tmp = getinfo_subindex(list_edsbuf, str_eventtimer, 'DefaultValue')
    str_tmp = '> Sub5 Event Timer (1ms) : ' + str_tmp + '\n'
    result = result + str_tmp
    #查询同步起始值
    str_tmp = getinfo_subindex(list_edsbuf, str_syncstartval, 'DefaultValue')
    str_tmp = '> Sub6 SYNC start value : ' + str_tmp + '\n'
    result = result + str_tmp
    result = result + '#### 映射' + '\n'
    result = result + '|PDO Sub-Inx|MAP Index(HEX)|MAP Sub-Inx| MAP Param Name | MAP Data Type|\n'
    result = result + '| ------- | ----------- | ------- | ------------ | ---------- |\n'

    if list_edsbuf.has_section(pdo_map_id) == True: #id存在
      eds_pdo_subinx_num = getinfo_subindex(list_edsbuf, pdo_map_id, 'SubNumber')
      #eds_pdo_subinx_num 有可能是16进制 0xaa
      if eds_pdo_subinx_num[0:2]=='0x':
        eds_pdo_subinx_num = int(eds_pdo_subinx_num,16)
      for index_sub_pdo in range (int(eds_pdo_subinx_num)):
        pdo_map_subid = pdo_map_id + 'sub' + str(index_sub_pdo+1)
        if list_edsbuf.has_section(pdo_map_subid) == True: #id存在
          if list_edsbuf.has_option(pdo_map_subid, 'DefaultValue') == False: #eds文件可能存在pdo不被映射的情况
            str_pdomapval = '0'
          else:
            str_pdomapval = getinfo_subindex(list_edsbuf, pdo_map_subid, 'DefaultValue')
          #str_pdomapval 有可能是16进制 0xaa
          if str_pdomapval[0:2] == '0x':
            str_pdomapval = int(str_pdomapval,16)
          else:
            str_pdomapval = int(str_pdomapval)
          if str_pdomapval == 0:
            map_index = ''
            map_subinx = ''
            map_datatype = ''
            pdo_map_name = ''
          else:
            hex_pdomapval = hex(int(str_pdomapval))
            map_index = '0x' + str(hex_pdomapval[2:6]).upper()
            map_subinx = '0x' + str(hex_pdomapval[6:8]).upper()
            pdo_map_IndexHEX = str(hex_pdomapval[2:6]).upper() # 大写
            if map_subinx == '0x00':
              pdo_map_name = getinfo_subindex(list_edsbuf, pdo_map_IndexHEX, 'ParameterName')
            else:
              pdo_map_SubIndexHEX = int(hex_pdomapval[6:8])
              if pdo_map_SubIndexHEX>15 :
                pdo_map_SubIndexHEX = '{:02X}'.format(pdo_map_SubIndexHEX)# subinxid 应为2Byte的16进制
              else:
                pdo_map_SubIndexHEX = '{:01X}'.format(pdo_map_SubIndexHEX)# subinxid 应为2Byte的16进制

              str_sdo_index = str(pdo_map_IndexHEX) + 'sub' + str(pdo_map_SubIndexHEX)
              #print(map_index, map_subinx, str_sdo_index)
              pdo_map_name = getinfo_subindex(list_edsbuf, str_sdo_index, 'ParameterName')
            map_datatype = get_pdomap_datatype(hex_pdomapval[8:10])
            #print('  PDOIndex:', pdo_map_id, '-', str(index_sub_pdo+1), '\tMapVal:', hex_pdomapval, '\tMapName:', pdo_map_name, '\tMaptype:', map_datatype)
          str_pdoval = '|' + str(index_sub_pdo+1) + '|' + map_index + '|' + map_subinx + '|' + pdo_map_name + '|' + map_datatype + '|\n'
          result = result + str_pdoval
  return result

def get_filepath(): # 通过对话框获取文件路径
  import tkinter as tk
  from tkinter import filedialog

  root = tk.Tk()
  root.withdraw()  # 隐藏主窗口
  file_types = [('EDS Files', '*.eds'), ('EDS Files', '*.eds'),('All files', '*')]
  file_pathname = filedialog.askopenfilename(title=app_name + ' v' + str(app_softverision) + ' | 请选择单个EDS文件', filetypes=file_types)
  file_path, file_name = os.path.split(file_pathname)
  return (file_pathname, file_path, file_name)

def parse():
  import configparser
  from tkinter import messagebox

  filepath_eds = get_filepath()
  file_save_path = filepath_eds[1] + '/'

  if filepath_eds[2]=='': #没有获取到文件，直接退出
    print('>', '[Err]','file is none')
    sys.exit(0)

  filename_eds = filepath_eds[0]
  list_eds = configparser.RawConfigParser()
  list_eds.read(filename_eds)

  if list_eds.has_section('FileInfo') == False:
    sys.exit(0) #没有检测到 FileInfo 退出
  eds_filename = getinfo_subindex(list_eds, 'FileInfo', 'FileName')
  eds_version = getinfo_subindex(list_eds, 'FileInfo', 'FileVersion')
  eds_revision = getinfo_subindex(list_eds, 'FileInfo', 'FileRevision')
  eds_moddate = getinfo_subindex(list_eds, 'FileInfo', 'ModificationDate')
  eds_modtime = getinfo_subindex(list_eds, 'FileInfo', 'ModificationTime')
  print('>', '[eds file name]',eds_filename)

  file_markdown_name = eds_filename[0:len(eds_filename)-4] + ' eds parse'
  file_save_path = file_save_path + file_markdown_name + '.md'
  #file_md=open(file_save_path,"w", encoding="utf-8") #文件写入

  # 打开文件，使用'w'模式写入，指定缓冲区大小为65536字节
  with open(file_save_path, "w", encoding="utf-8", buffering=65536) as file_md:
    file_md.write('# '+ file_markdown_name + '\n')

    file_md.write('## Device Information\n')
    file_info = '- File Version : ' + eds_version + '\n'
    file_md.write(file_info)
    file_info = '- File Revision : ' + eds_revision + '\n'
    file_md.write(file_info)
    file_info = '- Modification Date : ' + eds_moddate + '\n'
    file_md.write(file_info)
    file_info = '- Modification Time : ' + eds_modtime + '\n'
    file_md.write(file_info)

    #pdo映射
    str_pdomap = '''\n
## PDO Mapping\n
### RPDO(0x1400) Parameter\n
#### Sub1 COB-ID\n
- UNSIGNED32类型 PDO的COB-ID
- 最高位（位31）: 1-禁止该RPDO操作；0-使能该RPDO操作\n
- RPDO1-0x1400 值为 Node_ID + 0x100
- RPDO2-0x1401 值为 Node_ID + 0x200
- RPDO3-0x1402 值为 Node_ID + 0x300
- RPDO4-0x1403 值为 Node_ID + 0x400
#### Sub2 Transmission\n
- UNSIGNED8类型 传输类型
- 0x00 ~ 0xF0(0~240) 同步，只要接收到一个同步帧则将该RPDO数据更新到应用\n
- 0xF1 ~ 0xFD(253)，保留\n
- 0xFE(254)，异步，事件驱动，将接收到的数据直接更新到应用，制造商指定\n
- 0xFF(255)，异步，事件驱动，将接收到的数据直接更新到应用，设备协议和应用协议指定\n
- 注意：不被立马启用的RPDO，此处设为0xFF\n
#### Sub3 Inhibit Time\n
- UNSIGNED16类型 抑制时间
- 单位：100us\n
- 0-禁用，在PDO生效时，该值不可更改\n
#### Sub5 Event Timer\n
- UNSIGNED16类型 事件定时器
- 单位：1ms\n
- 0-表示禁用\n
- RPDO使用该事件进行期限监控，配置后接收到一帧RPDO激活期限监控，超时结果通知本地应用程序\n
#### Sub6 SYNC start value\n
- UNSIGNED8类型 同步起始值
- RPDOs不使用该值，读或写将导致SDO中止应答(0609 0011h)\n
### TPDO(0x1800) Parameter\n
#### Sub1 COB-ID\n
- UNSIGNED32类型 PDO的COB-ID
- 最高位（位31）: 1-禁止该TPDO操作；0-使能该TPDO操作\n
- TPDO1-0x1800 值为 Node_ID + 0x180
- TPDO2-0x1801 值为 Node_ID + 0x280
- TPDO3-0x1802 值为 Node_ID + 0x380
- TPDO4-0x1803 值为 Node_ID + 0x480
#### Sub2 Transmission\n
- UNSIGNED8类型 传输类型
- 0x00，同步，非周期，若映射对象数据改变，在下一个SYNC收到时发送\n
- 0x01 ~ 0xF0，同步，周期，只要接收到传输类型指定个数SYNC时，不管数据有无改变均发送该TPDO\n
- 0xF1 ~ 0xFB，保留\n
- 0xFC(252)，同步，仅RTR，每个SYNC开始采样，远程帧触发发送\n
- 0xFD(253)，异步，仅RTR，远程帧触发采样并直接生成PDO发送\n
- 0xFE(254)，异步，事件驱动，映射数据改变或者时间计时器到达则发送该TPDO，制造商指定（对象字典的制造商特定部分），任意时刻内部事件触发\n
- 0xFF(255)，异步，事件驱动，映射数据改变或者时间计时器到达则发送该TPDO，设备协议和应用协议指定，任意时刻内部事件触发\n
- 注意：不被立马启用的TPDO，此处设为0xFD\n
#### Sub3 Inhibit Time\n
- UNSIGNED16类型 抑制时间
- 单位：100us\n
- 0-表示禁用，在PDO生效时，该值不可更改\n
- 若传输类型(Sub2 Transmission)为0xFE或0xFF，抑制时间表示最小的PDO传输间隔，事件触发时的禁止发送时间间隔，防止总线占用严重。\n
#### Sub5 Event Timer\n
- UNSIGNED16类型 事件定时器
- 单位：1ms\n
- 0-表示禁用\n
- 定时发送的 PDO，它的定时时间，如果这个时间为 0，则 PDO 为事件改变发送\n
- 若传输类型(Sub2 Transmission)为0xFE或0xFF，抑制时间表示最大的PDO传输间隔，值为0表示事件触发，否则定时触发且事件触发，该值为定时时间\n
#### Sub6 SYNC start value\n
- UNSIGNED8类型 同步起始值
- 0-表示该PDO不处理SYNC帧的计数\n
- 1 ~ 0xF0(240)-表示该PDO处理SYNC帧的计数。若SYNC消息计数器未使能，此索引0x06值将被忽略。SYNC帧的计数值等于SYNC起始值时，视为收到第一帧SYNC\n
- 若设备进入NMT运行态，而检测到SYNC计数器值大于SYNC起始值时，设备必须等待下一轮正确的同步计数器值。\n\n
'''
    file_md.write(str_pdomap)

    file_md.write(eds_pdoparse_styleII(list_eds, 'RPDO'))
    file_md.write(eds_pdoparse_styleII(list_eds, 'TPDO'))

    #通讯参数
    file_md.write('## Communication Specific Parameters\n')
    file_md.write(eds_parse(list_eds, 'MandatoryObjects'))
    file_md.write(eds_parse(list_eds, 'OptionalObjects'))

    #读取厂家参数
    file_md.write('## Manufacturer Specific Parameters\n')
    file_md.write(eds_parse(list_eds, 'ManufacturerObjects'))

    #SDO错误代码
    str_sdoerrcode = '''
## SDO abort code error
| SDO abort code error(HEX) | 描述 |
| ------------------------- | ---- |
| 0x0503_0000 | 分段传输时，翻转位无变化 |
| 0x0504_0000 | SDO传输超时 |
| 0x0504_0001 | Client/server命令标识符无效或未知 |
| 0x0504_0002 | 无效的block尺寸(block mode only) |
| 0x0504_0003 | 无效的次序号(block mode only) |
| 0x0504_0004 | CRC错误(block mode only) |
| 0x0504_0005 | 内存溢出 |
| 0x0601_0000 | 不支持的对象访问 |
| 0x0601_0001 | 试图读只写对象 |
| 0x0601_0002 | 试图写只读对象 |
| 0x0602_0000 | 对象不在对象字典中 |
| 0x0604_0041 | 对象不能被映射到PDO上 |
| 0x0604_0042 | 意映射到PDO上的对象个数和长度超出PDO长度 |
| 0x0604_0043 | 普通参数不兼容原因 |
| 0x0604_0047 | 普通器件内部不兼容 |
| 0x0606_0000 | 由于硬件问题导致访问失败 |
| 0x0607_0010 | 数据类型不匹配，服务参数长度不匹配 |
| 0x0607_0012 | 数据类型不匹配，服务参数长度太高 |
| 0x0607_0013 | 数据类型不匹配，服务参数长度太低 |
| 0x0609_0011 | 子索引不存在 |
| 0x0609_0030 | 参数值超出范围(only for write access) |
| 0x0609_0031 | 参数超限，超出上限值 |
| 0x0609_0032 | 参数超限，超出下限值 |
| 0x0609_0036 | 最大值比最小值还小 |
| 0x0800_0000 | 一般错误 |
| 0x0800_0020 | 数据无法传输或存储到应用层 |
| 0x0800_0021 | 数据无法传输或存储到应用层，参考被本地控制 |
| 0x0800_0022 | 数据无法传输或存储到应用层，参考设备当前状态 |
| 0x0800_0023 | 对象字典动态生成失败，或当前对象字典不存在 |
'''
    file_md.write(str_sdoerrcode)

    file_md.flush() # 显式地刷新缓冲区，确保所有数据都被写入文件
    file_md.close()
  str_msg = '完成EDS文件解析，生成文件的路径为:' + file_save_path
  str_window_title = app_name + ' v' + str(app_softverision)
  str_window_title = str_window_title + ' - 提示'
  messagebox.showinfo(str_window_title, str_msg)  #确认信息框
  sys.exit(0)

# 下载文件url 另存为 savepathname
def download_url(url, savepathname):
  import wget

  if os.path.exists(savepathname) == True: #查找本地文件当前是否有这个文件
    try:
      os.remove(savepathname)
    except OSError as e:
      print('>', f"[错误] {savepathname}，错误信息: {e}")

  wget.download(url, savepathname)

def writerestartcmd(new_name,old_name):
  # import subprocess

  # #新程序启动时，删除旧程序制造的脚本
  # if os.path.isfile("upgrade.bat"):
  #   os.remove("upgrade.bat")

  b = open("upgrade.bat",'w')
  TempList = "@echo off\n"
  TempList += "if not exist " + new_name + " exit \n"  #判断是否有新版本的程序，没有就退出更新。
  TempList += "echo 正在更新至最新版本...\n"
  TempList += "timeout /t 3 /nobreak\n"  #等待3秒
  TempList += "del " + old_name + "\n" #删除旧程序
  TempList += "copy  " + new_name + " " + old_name + '\n' #复制新版本程序
  TempList += "del " + new_name + "\n" #删除下载缓存程序
  TempList += "echo 更新完成，正在启动...\n"
  TempList += "timeout /t 3 /nobreak\n"
  TempList += "start  " + old_name+"\n"
  TempList += "echo. & echo Finished.\n"
  TempList += "exit\n"
  b.write(TempList)
  b.close()
  #subprocess.Popen("upgrade.bat") #不显示cmd窗口
  os.system('start upgrade.bat')  #显示cmd窗口

def checkupdata(app_save_name:str):
  import urllib.request
  import re

  #新程序启动时，删除旧程序制造的脚本
  if os.path.isfile("upgrade.bat"):
    os.remove("upgrade.bat")

  # 获取版本信息 有更新则执行
  itemname = app_save_name
  url_latest = "https://gitee.com/Bite108/{}/releases/latest".format(itemname)

  print('>', 'check for software updates')
  #html用decode(‘utf-8’)进行解码，由bytes变成string，py3的urlopen返回的不是string是bytes
  try:
    res = urllib.request.urlopen(url_latest).read().decode('utf-8')
  except:
    print('>', 'the ethernet is not connection, skip check software verision')
    return False

  str_filier = f'<a href="/Bite108/{itemname}/releases/download/'
  str_filier = str_filier + '(.*?)">'
  url_app_tmp = re.compile(str_filier).findall(res)
  url_download = f'https://gitee.com/Bite108/{itemname}/releases/download/' + url_app_tmp[0]
  #print('latest url', url_download)

  verision_latest = url_app_tmp[0].split("/")
  print('>', 'curr:', str(app_softverision), '| latest:', verision_latest[0])

  if app_softverision >= int(verision_latest[0]):
    pass
    print('>', 'curr is latest')
  else:
    workpath = os.getcwd() #获取工作路径
    pathname_updata = workpath + '\\latest.exe'
    pathname_save = workpath + f'\\{app_save_name}.exe'
    download_url(url_download, pathname_updata)
    writerestartcmd(pathname_updata, pathname_save)
    sys.exit(0)

if __name__ == '__main__':
  print('\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>')
  print('>', 'app_softverision', app_name + ' v' + str(app_softverision))

  checkupdata('tools_eds_parse')
  parse()